<?php

namespace Giikin\TokenAuth;

use Giikin\TokenAuth\Constant\ErrorCode;
use Giikin\TokenAuth\Exception\RunnerException;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\HandlerStack;
use Hyperf\Guzzle\PoolHandler;
use Hyperf\Guzzle\RetryMiddleware;

class SSOUser
{
    /**
     * @var string
     */
    private $signFlag;
    /**
     * @var
     */
    private $applicationSystemId;
    /**
     * @var string
     */
    private $applicationSystemSecret;
    /**
     * @var string
     */
    private $appServiceUrl;

    /**
     * @var Client|null
     */
    private $client;


    private $uri = '/apis/index/index';

    /**
     * User constructor.
     * @param array $options
     */
    public function __construct(array $options = [])
    {
        $this->appServiceUrl = config('sso.app_service_url');
        $this->applicationSystemId = config('sso.app_id');
        $this->applicationSystemSecret = config('sso.app_secret');
        $this->signFlag = config('sso.app_sign_flag');
        $stack = HandlerStack::create(
            make(PoolHandler::class, ['option' => ['max_connections' => 1000]])
        );
        // 默认的重试Middleware
        $retry = make(RetryMiddleware::class, [
            'retries' => 1,
            'delay'   => 10,
        ]);
        $stack->push($retry->getMiddleware(), 'retry');
        $defaultOptions = [
            'allow_redirects' => false,  // 去除重定向
            'base_uri'        => $this->appServiceUrl,
            'timeout'         => 20,
            'expect'          => false,
            'handler'         => $stack,
        ];
        !empty($options) && $defaultOptions = array_merge($defaultOptions, $options);
        $this->client = make(Client::class, ['config' => $defaultOptions]);
    }

    /**
     * @param $token
     * @return mixed
     * @throws \Throwable
     */
    public function getUserMenus($token)
    {
        $service = $this->appServiceUrl . $this->uri . '?service=getMenus';
        return $this->invokeService($service, $token);
    }

    /**
     * @param $token
     * @return mixed
     * @throws \Throwable
     */
    public function getUserOrgs($token)
    {
        $service = $this->appServiceUrl . $this->uri . '?service=getOrgs';
        return $this->invokeService($service, $token);
    }

    /**
     * @param $token
     * @return mixed
     * @throws \Throwable
     */
    public function getUserInfo($token)
    {
        $sso = $this->appServiceUrl . $this->uri . '?service=getUserInfo';
        return $this->invokeService($sso, $token);
    }


    /**
     * @param $service
     * @param $token
     * @return mixed
     * @throws \Throwable
     */
    private function invokeService($service, $token)
    {
        $timestamp = time();
        $params = [
            '_SYSTEM'    => $this->applicationSystemId,
            '_TOKEN'     => $token,
            '_TIMESTAMP' => $timestamp,
            '_SIGN'      => $this->sign($token, $timestamp)
        ];
        try {
            $options = ['form_params' => $params];
            $response = $this->client->post($service, $options);
            if ($response->getStatusCode() == 301 || $response->getStatusCode() == 302) {
                $message = "oss." . $response->getStatusCode() . "HTTP Location: " . $response->getHeaderLine('location');
                throw new RunnerException(ErrorCode::AUTH_ERROR, $message);
            }
            return json_decode($response->getBody()->getContents(), true);
        } catch (\Throwable $e) {
            if ($e instanceof GuzzleException) {
                throw new RunnerException(ErrorCode::RETRY_MANY_TIMES, $e->getMessage());
            }
            throw $e;
        }
    }

    /**
     * @param $token
     * @param $timestamp
     * @return string
     */
    private function sign($token, $timestamp)
    {
        return md5($this->signFlag . $this->applicationSystemSecret . $timestamp . $token);
    }
}
